﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using XnaAux;

namespace PrisonStep
{
    public class AnimationPlayer
    {
        #region Member Variables

        private struct BoneInfo : AnimationClips.Bone
        {
            private int mCurrentKeyFrame;   // Current Keyframe for the bone
            private bool mValid;

            private Quaternion mRotation;
            private Vector3 mTranslation;

            public int CurrentKeyFrame { get { return mCurrentKeyFrame; } set { mCurrentKeyFrame = value; } }
            public bool Valid { get { return mValid; } set { mValid = value; } }
            public Quaternion Rotation { get { return mRotation; } set { mRotation = value; } }
            public Vector3 Translation { get { return mTranslation; } set { mTranslation = value; } }
        }

        private AnimationClips.Clip mClip = null;

        private MouseState mLastMouseState;

        private BoneInfo[] mBoneInfos;

        private float mWindowSize = 0;

        private AnimatedModel mModel = null;

        #endregion

        #region Properties

        public AnimationClips.Bone GetBone(int b) { return mBoneInfos[b]; }

        public float WindowSize { get { return mWindowSize; } set { mWindowSize = value; } }

        public bool Finished { get { return mClip.Duration <= mClip.Time; } }

        public double Speed { get { return mClip.Speed; } set { mClip.Speed = value; } }

        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="aClip"></param>
        public AnimationPlayer(AnimatedModel aModel,AnimationClips.Clip aClip)
        {
            this.mClip = aClip;

            this.mModel = aModel;
            Initialize();
        }

        /// <summary>
        /// Initialize
        /// </summary>
        public void Initialize()
        {
            mClip.BoneCount = mClip.Keyframes.Length;
            mBoneInfos = new BoneInfo[mClip.BoneCount];

            mClip.Looping = false;

            mClip.Time = 0;
            
        }

        /// <summary>
        /// Update the clip associated with the player.
        /// </summary>
        /// <param name="aDelta"></param>
        public void Update(double aDelta)
        {
            
            MouseState lMouseState = Mouse.GetState();
            if (lMouseState.RightButton == ButtonState.Pressed &&
                mLastMouseState.RightButton == ButtonState.Pressed)
            {
                if (lMouseState.Y < mWindowSize && lMouseState.Y > -1)
                {
                    mClip.Speed = 2.0 * (1.0 - ((double)(lMouseState.Y / mWindowSize)));
                    //Ensures that the model speed gets set to 0 if we reach the bottom of the page
                    if (mLastMouseState.Y == mWindowSize)
                        mClip.Speed = 0;

                }
            }

            mClip.Time += (aDelta * mClip.Speed);
            for (int b = 0; b < mClip.BoneCount; b++)
            {
                List<AnimationClips.Keyframe> lKeyframes = mClip.Keyframes[b];
                if (lKeyframes.Count == 0)
                    continue;

                // The time needs to be greater than or equal to the
                // current keyframe time and less than the next keyframe
                // time.

                while (mBoneInfos[b].CurrentKeyFrame < 0 ||
                    (mBoneInfos[b].CurrentKeyFrame < lKeyframes.Count - 1 &&
                    lKeyframes[mBoneInfos[b].CurrentKeyFrame + 1].Time <= mClip.Time))
                {
                    mBoneInfos[b].CurrentKeyFrame++;
                }

                //
                // Update the Bone
                //

                // Our animation is a looping one
                if (mClip.Looping)
                {
                    // the animation has finished the last keyframe and now will start over
                    if (mBoneInfos[b].CurrentKeyFrame == lKeyframes.Count - 1)
                    {
                        Initialize();
                    }
                }
                
                int c = mBoneInfos[b].CurrentKeyFrame;
                if (c >= 0 && mBoneInfos[b].CurrentKeyFrame != lKeyframes.Count - 1)
                {
                    AnimationClips.Keyframe lKeyframe = lKeyframes[c];
                    mBoneInfos[b].Valid = true;
                    float t = (float)((mClip.Time - lKeyframe.Time)/
                                       (lKeyframes[c+1].Time - lKeyframe.Time));
                    
                    Quaternion lRotation = Quaternion.Slerp(lKeyframe.Rotation, lKeyframes[c+1].Rotation, t);
                    Vector3 lTranslation = Vector3.Lerp(lKeyframe.Translation, lKeyframes[c + 1].Translation, t);
                    mBoneInfos[b].Rotation = lRotation;
                    mBoneInfos[b].Translation = lTranslation;
                    
                }
            }
            mLastMouseState = lMouseState;
        }

    }
}
